﻿
//#define NULL_CHECK_MAC
/* There is a null check for the MAC Key's on the functions for SkeinMAC despite the fact that 
 * the algorithm will work if there is a null passed (it will behave as if it were a regular hash).
 * Example: SkeinMAC256(input, NULL, X) = Skein256(input, X)
 * NULL_CHECK_MAC does not allow this equality behavior, but the Known Answer Tests will fail.
 * This *should* be defined, as an unexpected null or a zero- or -1-length array (an array
 * with NO elements) can produce results that may leak information or allow an attacker to produce 
 * hashes for a different system than intended.  
 * One way to mitigate this further is to use the personalization string for specific implementations
 * per the white paper.
*/
using System;

namespace SkeinLibManaged
{
    public partial class Skein
    {
        //==BASIC===============================================================================================
        #region "Basic Hashing stubs."
        
        /// <summary>
        /// Skein Hash Algorithm in the Narrow (256-bit) state-width. 
        /// </summary>
        /// <param name="input">Input string of data to be hashed.</param>
        /// <param name="hashBitLength">Length, in bits, of the hash value for the data.  Between 8 and 65536 in 8 bit increments.</param>
        /// <returns>Returns byte array of resulting hash of data.</returns>
        public static byte[] Skein256(string input, UInt16 hashBitLength)
        {
            if (hashBitLength == 0)
                throw new System.ArgumentOutOfRangeException("Skein256String: HashBitLength cannot be ZERO.");
            if (hashBitLength % 8 != 0)
                throw new System.NotSupportedException("Skein256String: HashBitLength must be within Byte alignment (length mod 8 = 0).");
            System.Text.UTF8Encoding convertMe = new System.Text.UTF8Encoding();
            byte[] localBytes = convertMe.GetBytes(input);
            return SkeinBKCommonBytes(StateSize.Narrow, localBytes, null, hashBitLength);
        }

        /// <summary>
        /// Skein Hash Algorithm in the Narrow (256-bit) state-width.
        /// </summary>
        /// <param name="input">Input byte array of data to be hashed. Partial byte inputs not supported.</param>
        /// <param name="hashBitLength">Length, in bits, of the hash value for the data.  Between 8 and 65536 in 8 bit increments.</param>
        /// <returns>Returns byte array of resulting hash of data.</returns>
        public static byte[] Skein256(byte[] input, UInt16 hashBitLength)
        {
            if (hashBitLength == 0)
                throw new System.ArgumentOutOfRangeException("Skein256: HashBitLength cannot be ZERO.");
            if (hashBitLength % 8 != 0)
                throw new System.NotSupportedException("Skein256: HashBitLength must be within Byte alignment (length mod 8 = 0).");
            return SkeinBKCommonBytes(StateSize.Narrow, input, null, hashBitLength);
        }

        /// <summary>
        /// Skein Hash Algorithm in the Medium (512-bit) state-width. 
        /// </summary>
        /// <param name="input">Input string of data to be hashed.</param>
        /// <param name="hashBitLength">Length, in bits, of the hash value for the data.  Between 8 and 65536 in 8 bit increments.</param>
        /// <returns>Returns byte array of resulting hash of data.</returns>
        public static byte[] Skein512(string input, UInt16 hashBitLength)
        {
            if (hashBitLength == 0)
                throw new System.ArgumentOutOfRangeException("Skein512String: HashBitLength cannot be ZERO.");
            if (hashBitLength % 8 != 0)
                throw new System.NotSupportedException("Skein512String: HashBitLength must be within Byte alignment (length mod 8 = 0).");
            System.Text.UTF8Encoding convertMe = new System.Text.UTF8Encoding();
            byte[] localBytes = convertMe.GetBytes(input);
            return SkeinBKCommonBytes(StateSize.Medium, localBytes, null, hashBitLength);
        }

        /// <summary>
        /// Skein Hash Algorithm in the Medium (512-bit) state-width.
        /// </summary>
        /// <param name="input">Input byte array of data to be hashed. Partial byte inputs not supported.</param>
        /// <param name="hashBitLength">Length, in bits, of the hash value for the data.  Between 8 and 65536 in 8 bit increments.</param>
        /// <returns>Returns byte array of resulting hash of data.</returns>
        public static byte[] Skein512(byte[] input, UInt16 hashBitLength)
        {
            if (hashBitLength == 0)
                throw new System.ArgumentOutOfRangeException("Skein512: HashBitLength cannot be ZERO.");
            if (hashBitLength % 8 != 0)
                throw new System.NotSupportedException("Skein512: HashBitLength must be within Byte alignment (length mod 8 = 0).");
            return SkeinBKCommonBytes(StateSize.Medium, input, null, hashBitLength);
        }

        /// <summary>
        /// Skein Hash Algorithm in the Wide (1024-bit) state-width. 
        /// </summary>
        /// <param name="input">Input string of data to be hashed.</param>
        /// <param name="hashBitLength">Length, in bits, of the hash value for the data.  Between 8 and 65536 in 8 bit increments.</param>
        /// <returns>Returns byte array of resulting hash of data.</returns>
        public static byte[] Skein1024(string input, UInt16 hashBitLength)
        {
            if (hashBitLength == 0)
                throw new System.ArgumentOutOfRangeException("Skein1024String: HashBitLength cannot be ZERO.");
            if (hashBitLength % 8 != 0)
                throw new System.NotSupportedException("Skein1024String: HashBitLength must be within Byte alignment (length mod 8 = 0).");
            System.Text.UTF8Encoding convertMe = new System.Text.UTF8Encoding();
            byte[] localBytes = convertMe.GetBytes(input);
            return SkeinBKCommonBytes(StateSize.Wide, localBytes, null, hashBitLength);
        }

        /// <summary>
        /// Skein Hash Algorithm in 1024-bit state-width.
        /// </summary>
        /// <param name="input">Input byte array of data to be hashed. Partial byte inputs not supported.</param>
        /// <param name="hashBitLength">Length, in bits, of the hash value for the data.  Between 8 and 65536 in 8 bit increments.</param>
        /// <returns>Returns byte array of resulting hash of data.</returns>
        public static byte[] Skein1024(byte[] input, UInt16 hashBitLength)
        {
            if (hashBitLength == 0)
                throw new System.ArgumentOutOfRangeException("Skein1024: HashBitLength cannot be ZERO.");
            if (hashBitLength % 8 != 0)
                throw new System.NotSupportedException("Skein1024: HashBitLength must be within Byte alignment (length mod 8 = 0).");
            return SkeinBKCommonBytes(StateSize.Wide, input, null, hashBitLength);
        }

        #endregion
        //==MACKey/KEYED========================================================================================
        #region "Keyed Function stubs for MAC's."
        
        #region "Narrow/256"

        /// <summary>
        /// Keyed Skein Hash Algorithm in 256-bit state-width for making Message Authentication Codes (MAC's).
        /// </summary>
        /// <param name="input">Input string of data to be hashed.</param>
        /// <param name="MACKey">Input string of key data for producing a MAC.</param>
        /// <param name="hashBitLength">Length, in bits, of the hash value for the data.  Between 8 and 65536 in 8 bit increments.</param>
        /// <returns>Returns byte array of resulting MAC of data and key.</returns>
        public static byte[] SkeinMAC256(string input, string MACKey, UInt16 hashBitLength)
        {
            if (hashBitLength == 0)
                throw new System.ArgumentOutOfRangeException("SkeinMAC256: HashBitLength cannot be ZERO.");
            if (hashBitLength % 8 != 0)
                throw new System.NotSupportedException("SkeinMAC256: HashBitLength must be within Byte alignment (length mod 8 = 0).");
#if NULL_CHECK_MAC           
            if (MACKey == null)
                throw new System.ArgumentException("SkeinMAC256: MAC Key cannot be null or zero length, use Skein256 instead (security risk).");
#endif        
            System.Text.UTF8Encoding convertMe = new System.Text.UTF8Encoding();
            byte[] localBytes = convertMe.GetBytes(input);
            byte[] localMAC = convertMe.GetBytes(MACKey);
            return SkeinBKCommonBytes(StateSize.Narrow, localBytes, localMAC, hashBitLength);
        }

        /// <summary>
        /// Keyed Skein Hash Algorithm in 256-bit state-width for making Message Authentication Codes (MAC's).
        /// </summary>
        /// <param name="input">Input string of data to be hashed.</param>
        /// <param name="MACKey">Input byte array of key data for producing a MAC.</param>
        /// <param name="hashBitLength">Length, in bits, of the hash value for the data.  Between 8 and 65536 in 8 bit increments.</param>
        /// <returns>Returns byte array of resulting MAC of data and key.</returns>
        public static byte[] SkeinMAC256(string input, byte[] MACKey, UInt16 hashBitLength)
        {
            if (hashBitLength == 0)
                throw new System.ArgumentOutOfRangeException("SkeinMAC256: HashBitLength cannot be ZERO.");
            if (hashBitLength % 8 != 0)
                throw new System.NotSupportedException("SkeinMAC256: HashBitLength must be within Byte alignment (length mod 8 = 0).");
#if NULL_CHECK_MAC           
            if (MACKey == null)
                throw new System.ArgumentException("SkeinMAC256: MAC Key cannot be null or zero length, use Skein256 instead (security risk).");
#endif
            System.Text.UTF8Encoding convertMe = new System.Text.UTF8Encoding();
            byte[] localBytes = convertMe.GetBytes(input);
            return SkeinBKCommonBytes(StateSize.Narrow, localBytes, MACKey, hashBitLength);
        }

        /// <summary>
        /// Keyed Skein Hash Algorithm in 256-bit state-width for making Message Authentication Codes (MAC's).
        /// </summary>
        /// <param name="input">Input byte array of data to be hashed. Partial byte inputs not supported.</param>
        /// <param name="MACKey">Input byte array of key data for producing a MAC.</param>
        /// <param name="hashBitLength">Length, in bits, of the hash value for the data.  Between 8 and 65536 in 8 bit increments.</param>
        /// <returns>Returns byte array of resulting MAC of data and key.</returns>
        public static byte[] SkeinMAC256(byte[] input, byte[] MACKey, UInt16 hashBitLength)
        {
            if (hashBitLength == 0)
                throw new System.ArgumentOutOfRangeException("SkeinMAC256: HashBitLength cannot be ZERO.");
            if (hashBitLength % 8 != 0)
                throw new System.NotSupportedException("SkeinMAC256: HashBitLength must be within Byte alignment (length mod 8 = 0).");        
#if NULL_CHECK_MAC           
            if (MACKey == null)
                throw new System.ArgumentException("SkeinMAC256: MAC Key cannot be null or zero length, use Skein256 instead (security risk).");
#endif        
            return SkeinBKCommonBytes(StateSize.Narrow, input, MACKey, hashBitLength);
        }

        #endregion //Narrow/256
        
        #region "Medium/512"

        /// <summary>
        /// Keyed Skein Hash Algorithm in 512-bit state-width for making Message Authentication Codes (MAC's).
        /// </summary>
        /// <param name="input">Input string of data to be hashed.</param>
        /// <param name="MACKey">Input string of key data for producing a MAC.</param>
        /// <param name="hashBitLength">Length, in bits, of the hash value for the data.  Between 8 and 65536 in 8 bit increments.</param>
        /// <returns>Returns byte array of resulting MAC of data and key.</returns>
        public static byte[] SkeinMAC512(string input, string MACKey, UInt16 hashBitLength)
        {
            if (hashBitLength == 0)
                throw new System.ArgumentOutOfRangeException("SkeinMAC512: HashBitLength cannot be ZERO.");
            if (hashBitLength % 8 != 0)
                throw new System.NotSupportedException("SkeinMAC512: HashBitLength must be within Byte alignment (length mod 8 = 0).");
#if NULL_CHECK_MAC           
            if (MACKey == null)
                throw new System.ArgumentException("SkeinMAC512: MAC Key cannot be null or zero length, use Skein512 instead (security risk).");
#endif
            System.Text.UTF8Encoding convertMe = new System.Text.UTF8Encoding();
            byte[] localBytes = convertMe.GetBytes(input);
            byte[] localMAC = convertMe.GetBytes(MACKey);
            return SkeinBKCommonBytes(StateSize.Medium, localBytes, localMAC, hashBitLength);
        }

        /// <summary>
        /// Keyed Skein Hash Algorithm in 512-bit state-width for making Message Authentication Codes (MAC's).
        /// </summary>
        /// <param name="input">Input string of data to be hashed.</param>
        /// <param name="MACKey">Input byte array of key data for producing a MAC.</param>
        /// <param name="hashBitLength">Length, in bits, of the hash value for the data.  Between 8 and 65536 in 8 bit increments.</param>
        /// <returns>Returns byte array of resulting MAC of data and key.</returns>
        public static byte[] SkeinMAC512(string input, byte[] MACKey, UInt16 hashBitLength)
        {
            if (hashBitLength == 0)
                throw new System.ArgumentOutOfRangeException("SkeinMAC512: HashBitLength cannot be ZERO.");
            if (hashBitLength % 8 != 0)
                throw new System.NotSupportedException("SkeinMAC512: HashBitLength must be within Byte alignment (length mod 8 = 0).");
#if NULL_CHECK_MAC           
            if (MACKey == null)
                throw new System.ArgumentException("SkeinMAC512: MAC Key cannot be null or zero length, use Skein512 instead (security risk).");
#endif
            System.Text.UTF8Encoding convertMe = new System.Text.UTF8Encoding();
            byte[] localBytes = convertMe.GetBytes(input);
            return SkeinBKCommonBytes(StateSize.Medium, localBytes, MACKey, hashBitLength);
        }

        /// <summary>
        /// Keyed Skein Hash Algorithm in 512-bit state-width for making Message Authentication Codes (MAC's).
        /// </summary>
        /// <param name="input">Input byte array of data to be hashed. Partial byte inputs not supported.</param>
        /// <param name="MACKey">Input byte array of key data for producing a MAC.</param>
        /// <param name="hashBitLength">Length, in bits, of the hash value for the data.  Between 8 and 65536 in 8 bit increments.</param>
        /// <returns>Returns byte array of resulting MAC of data and key.</returns>
        public static byte[] SkeinMAC512(byte[] input, byte[] MACKey, UInt16 hashBitLength)
        {
            if (hashBitLength == 0)
                throw new System.ArgumentOutOfRangeException("SkeinMAC512: HashBitLength cannot be ZERO.");
            if (hashBitLength % 8 != 0)
                throw new System.NotSupportedException("SkeinMAC512: HashBitLength must be within Byte alignment (length mod 8 = 0).");
#if NULL_CHECK_MAC     
            if (MACKey == null)
                throw new System.ArgumentException("SkeinMAC512: MAC Key cannot be null or zero length, use Skein512Bytes instead (security risk).");
#endif
            return SkeinBKCommonBytes(StateSize.Medium, input, MACKey, hashBitLength);
        }

        #endregion //Medium/512

        #region "Wide/1024"

        /// <summary>
        /// Keyed Skein Hash Algorithm in 1024-bit state-width for making Message Authentication Codes (MAC's).
        /// </summary>
        /// <param name="input">Input string of data to be hashed.</param>
        /// <param name="MACKey">Input string of key data for producing a MAC.</param>
        /// <param name="hashBitLength">Length, in bits, of the hash value for the data.  Between 8 and 65536 in 8 bit increments.</param>
        /// <returns>Returns byte array of resulting MAC of data and key.</returns>
        public static byte[] SkeinMAC1024(string input, string MACKey, UInt16 hashBitLength)
        {
            if (hashBitLength == 0)
                throw new System.ArgumentOutOfRangeException("SkeinMAC1024: HashBitLength cannot be ZERO.");
            if (hashBitLength % 8 != 0)
                throw new System.NotSupportedException("SkeinMAC1024: HashBitLength must be within Byte alignment (length mod 8 = 0).");
#if NULL_CHECK_MAC           
            if (MACKey == null)
                throw new System.ArgumentException("SkeinMAC1024: MAC Key cannot be null or zero length, use Skein1024 instead (security risk).");
#endif
            System.Text.UTF8Encoding convertMe = new System.Text.UTF8Encoding();
            byte[] localBytes = convertMe.GetBytes(input);
            byte[] localMAC = convertMe.GetBytes(MACKey);
            return SkeinBKCommonBytes(StateSize.Wide, localBytes, localMAC, hashBitLength);
        }

        /// <summary>
        /// Keyed Skein Hash Algorithm in 1024-bit state-width for making Message Authentication Codes (MAC's).
        /// </summary>
        /// <param name="input">Input string of data to be hashed.</param>
        /// <param name="MACKey">Input byte array of key data for producing a MAC.</param>
        /// <param name="hashBitLength">Length, in bits, of the hash value for the data.  Between 8 and 65536 in 8 bit increments.</param>
        /// <returns>Returns byte array of resulting MAC of data and key.</returns>
        public static byte[] SkeinMAC1024(string input, byte[] MACKey, UInt16 hashBitLength)
        {
            if (hashBitLength == 0)
                throw new System.ArgumentOutOfRangeException("SkeinMAC1024: HashBitLength cannot be ZERO.");
            if (hashBitLength % 8 != 0)
                throw new System.NotSupportedException("SkeinMAC1024: HashBitLength must be within Byte alignment (length mod 8 = 0).");
#if NULL_CHECK_MAC           
            if (MACKey == null)
                throw new System.ArgumentException("SkeinMAC1024: MAC Key cannot be null or zero length, use Skein1024 instead (security risk).");
#endif
            System.Text.UTF8Encoding convertMe = new System.Text.UTF8Encoding();
            byte[] localBytes = convertMe.GetBytes(input);
            return SkeinBKCommonBytes(StateSize.Wide, localBytes, MACKey, hashBitLength);
        }

        /// <summary>
        /// Keyed Skein Hash Algorithm in 1024-bit state-width for making Message Authentication Codes (MAC's).
        /// </summary>
        /// <param name="input">Input byte array of data to be hashed. Partial byte inputs not supported.</param>
        /// <param name="MACKey">Input byte array of key data for producing a MAC.</param>
        /// <param name="hashBitLength">Length, in bits, of the hash value for the data.  Between 8 and 65536 in 8 bit increments.</param>
        /// <returns>Returns byte array of resulting MAC of data and key.</returns>
        public static byte[] SkeinMAC1024(byte[] input, byte[] MACKey, UInt16 hashBitLength)
        {
            if (hashBitLength == 0)
                throw new System.ArgumentOutOfRangeException("SkeinMAC1024: HashBitLength cannot be ZERO.");
            if (hashBitLength % 8 != 0)
                throw new System.NotSupportedException("SkeinMAC1024: HashBitLength must be within Byte alignment (length mod 8 = 0).");
 #if NULL_CHECK_MAC     
            if (MACKey == null)
                throw new System.ArgumentException("SkeinMAC1024: MAC Key cannot be null or zero length, use Skein1024Bytes instead (security risk).");
#endif
            return SkeinBKCommonBytes(StateSize.Wide, input, MACKey, hashBitLength);
        }

        #endregion //Wide/1024

        #endregion //Keyed Function stubs for MAC's.
    }
}